/**
 * 
 */
package demo.dcs;


import demo.dcs.util.ExceptionUtil;
import demo.java.lang.management.jvm.AppTraceConfig;
import demo.java.lang.management.jvm.LocalAsySendImpl2;
import demo.java.lang.management.vo.Annotation;
import demo.java.lang.management.vo.BinaryAnnotation;
import demo.java.lang.management.vo.XmlyLogSpan;
import demo.java.lang.management.vo.XmlySpan;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * @author mandy.hu
 * 
 */
public class TraceGenerater2 {

	private static Logger logger = LoggerFactory.getLogger(TraceGenerater2.class);

	private GenerateTraceId generateTraceId = new DefaultGenerateTraceId();
	
	private GenerateTraceId generateTraceId2 = new XUIDGenerateTraceId();
	
	private LocalAsySendImpl2 asySendService = LocalAsySendHolder.getInstance2();
	
	private MertricSendSchedule mertricSendSchedule = MertricSendSchedule.getMertricSendSchedule();
	
	private ThreadLocal<XmlySpan> spanThreadLocal = new ThreadLocal<XmlySpan>();
	
	private ThreadLocal<XmlySpan> mainstaySpanThreadLocal = new ThreadLocal<XmlySpan>();

	private Sampler sampler = new SampleImpl2();

	private TraceGenerater2() {

	}
	public boolean isSample() {
	    return (asySendService != null && asySendService.isReady()) && sampler.isSample();
	}
    public static boolean isTraceEnable() {
        return true;
    }
    
    public void removeParentSpan() {
        spanThreadLocal.remove();
    }

    public XmlySpan getParentSpan() {
        return spanThreadLocal.get();
    }

    public void setParentSpan(XmlySpan span) {
        spanThreadLocal.set(span);
    }
    
    public void removeMainstaySpan() {
    	mainstaySpanThreadLocal.remove();
    }

    public XmlySpan getMainstaySpan() {
        return mainstaySpanThreadLocal.get();
    }

    public void setMainstaySpan(XmlySpan span) {
    	mainstaySpanThreadLocal.set(span);
    }
    
    //注意ted不能调用这个方法
    public void addProperties(String key, String value) {
    	XmlySpan span = spanThreadLocal.get();
        if (span != null) {
        	span.addProp(key, value);
        }
    }

	private static class TraceHolder {
		static TraceGenerater2 instance = new TraceGenerater2();
	}

	public static TraceGenerater2 getTracer() {
		return TraceHolder.instance;
	}
	
	public void writeSpan(XmlySpan xmlySpan) {
		asySendService.asySend(xmlySpan);
	}
	
	public String getMonitorIdThreadLocal() {
		XmlySpan span = spanThreadLocal.get();
        if (span != null) {
           return span.getMonitorId();
        }
        return null;
	}
	public String getMonitorPIdThreadLocal() {
		XmlySpan span = spanThreadLocal.get();
        if (span != null) {
           return span.getMonitorPId();
        }
        return null;
	}
	public String getTraceIdThreadLocal() {
		XmlySpan span = spanThreadLocal.get();
        if (span != null) {
           return span.getTraceId();
        }
        return null;
	}
    public String getSpanIdThreadLocal(){
    	XmlySpan span = spanThreadLocal.get();
        if (span != null) {
        	return span.getSpanId();
        }
        return null;
	}
    public String getServiceIdThreadLocal(){
    	XmlySpan span = spanThreadLocal.get();
        if (span != null) {
            return span.getServiceId();
        }
        return null;
    }
    public String getXmlyIdThreadLocal(){
        XmlySpan span = spanThreadLocal.get();
        if (span != null) {
            return span.getXmlyId();
        }
        return null;
    }

    //构建xmlySpan
    public XmlySpan newSpan(String traceId, String spanId, String parentId) {
        return newSpan(null, traceId, spanId, parentId);
    }
	
	//构建xmlySpan
    public XmlySpan newSpan(String appName, String traceId, String spanId, String parentId) {
    	XmlySpan span = new XmlySpan();
        if (StringUtils.isNotBlank(appName)){
            span.setAppName(appName);
        }
        if (StringUtils.isNotBlank(traceId)) {
        	span.setTraceId(traceId);
		} else {
			span.setTraceId(genTracerId());
		}
        
        if (StringUtils.isNotBlank(spanId)) {
        	span.setParentId(spanId);
		}
        span.setSpanId(genSpanId());
        span.setTs(System.currentTimeMillis());
        
        boolean s = isSample();
        span.setIsSample(s);
        
        return span;
    }

	public void dealwithNewSpan(String spanId, String monitorId, XmlySpan span) {

		if (StringUtils.isNotBlank(spanId)) {
			span.setSpanId(spanId);
		} else {
			span.setSpanId(genTracerId());
		}

		if (StringUtils.isNotBlank(monitorId)) {
			span.setMonitorPId(monitorId);
		}
		span.setMonitorId(genSpanId());
	}
    //构建Span，参数通过上游接口传递过来
    public XmlySpan genSpan(String appName, String traceId, String pid, String id, String spanName, String serviceId,String ip) {
        XmlySpan span = new XmlySpan();
        span.setAppName(appName);
        span.setSpanId(id);
        span.setParentId(pid);
        span.setSpanName(spanName);
        span.setTraceId(traceId);
        span.setServiceId(serviceId);
        span.setIp(ip);
        span.setTs(System.currentTimeMillis());
        return span;
    }
    
	public String genTracerId() {

		return generateTraceId2.generateTraceId();
	}

	public String genSpanId() {

		return generateTraceId2.generateSpanId();
	}
	public String genLogId() {

		return generateTraceId2.generateSpanId();
	}

	public void writeLogSpan(XmlyLogSpan span) {
		asySendService.asySendLogSpan(span);
	}


	public void writeMonitorSpan(XmlySpan span) {
		asySendService.asyMonitorSend(span);
	}
	
   public void asyCatchUnExceptionLogSpan(Throwable throwable, String detail) {
        XmlyLogSpan span = new XmlyLogSpan();
        span.setTraceId(TraceGenerater2.getTracer().getTraceIdThreadLocal());
        span.setSpanId(TraceGenerater2.getTracer().getSpanIdThreadLocal());
        span.setServiceId(TraceGenerater2.getTracer().getServiceIdThreadLocal());
        span.setLevel("ERROR");
        span.setThreadId(Thread.currentThread().getName());
        span.setTimestamp(System.currentTimeMillis());
        span.setApp(AppTraceConfig.getLocalConfig().getAppName());
        span.setHostName(AppTraceConfig.getLocalConfig().getHostName());
        span.setIp(AppTraceConfig.getLocalConfig().getIp());
        span.setLogDetail(ExceptionUtil.getExceptionDetail(throwable));
        span.setLogIdentity(throwable.getClass().getName());
        span.setProcessId(AppTraceConfig.getLocalConfig().getProcessId());

        StackTraceElement[] stackTrace = throwable.getStackTrace();
        if (stackTrace.length == 0) {
            span.setMethod("unknown");
        } else {
            StackTraceElement lastElem = stackTrace[0]; // ArrayOutOfBoundException
            span.setMethod(lastElem.getClassName() + ":" + lastElem.getMethodName());
            span.setLogIdentity(lastElem.getClassName() + ":" + lastElem.getMethodName() + ":" + lastElem.getLineNumber());
        }
        asySendService.asySendLogSpan(span);
    }
   
   //构建cs annotation
   public void clientSendRecord(XmlySpan span,long start) {
       Annotation annotation = new Annotation();
       annotation.setValue(Annotation.CLIENT_SEND);
       annotation.setTimestamp(start);
       span.addAnnotation(annotation);
   }

   //构建cr annotation
   public void clientReceiveRecord(XmlySpan span, long end,long duration) {
       Annotation annotation = new Annotation();
       annotation.setValue(Annotation.CLIENT_RECEIVE);
       annotation.setTimestamp(end);
       annotation.setDuration(duration);
       span.addAnnotation(annotation);
       asySendService.asyMonitorSend(span);
   }

   //构建sr annotation
   public void serverReceiveRecord(XmlySpan span, long start) {
       Annotation annotation = new Annotation();
       annotation.setValue(Annotation.SERVER_RECEIVE);
       annotation.setTimestamp(start);
       span.addAnnotation(annotation);
       spanThreadLocal.set(span);
   }

   //构建 ss annotation
   public void serverSendRecord(XmlySpan span,long end,int duration) {
       Annotation annotation = new Annotation();
       annotation.setTimestamp(end);
       annotation.setValue(Annotation.SERVER_SEND);
       annotation.setDuration(Long.valueOf(duration));
       span.addAnnotation(annotation);
       asySendService.asyMonitorSend(span);
   }
   
   public void addBinaryAnnotation(BinaryAnnotation b) {
       XmlySpan span = spanThreadLocal.get();
       if (span != null) {
           span.addBinaryAnnotation(b);
       }
   }
   //构建xmlySpan
   public XmlySpan newMonitorSpan(String spanId, String monitorId, String monitorPId, String serverIp) {
       return newMonitorSpan(null, spanId, monitorId, monitorPId, serverIp);
   }
	
	//构建xmlySpan
   public XmlySpan newMonitorSpan(String appName, String spanId, String monitorId, String monitorPId, String serverIp) {
   	   XmlySpan span = new XmlySpan();
       if (StringUtils.isNotBlank(appName)){
           span.setAppName(appName);
       }
       if (StringUtils.isNotBlank(spanId)) {
       	span.setSpanId(spanId);
		} else {
			span.setSpanId(genTracerId());
		}
       
       if (StringUtils.isNotBlank(monitorId)) {
       	span.setMonitorId(monitorId);
		}else {
           span.setMonitorId(genSpanId());
       }
       span.setMonitorPId(monitorPId);
       span.setTs(System.currentTimeMillis());
       span.setIp(serverIp);
       return span;
   }
   //构建Span，参数通过上游接口传递过来
   public XmlySpan genMonitorSpan(String appName, String traceId, String spanId, String monitorPId, String monitorId, String spanName, String serviceId,String ip) {
       XmlySpan span = new XmlySpan();
       span.setAppName(appName);
       span.setTraceId(traceId);
       span.setSpanId(spanId);
       span.setMonitorId(monitorId);
       span.setMonitorPId(monitorPId);
       span.setSpanName(spanName);
       span.setServiceId(serviceId);
       span.setIp(ip);
       span.setTs(System.currentTimeMillis());
       XmlySpan span1 = TraceGenerater2.getTracer().getParentSpan();
       if (span1!=null) {
    	   span.setIsSample(span1.getIsSample());
	   }
       return span;
   }
   public void dealwithNewSpan(XmlySpan newSpan, XmlySpan oldSpan) {
	   if (oldSpan!=null) {
		   newSpan.setTraceId(oldSpan.getTraceId());
		   newSpan.setParentId(oldSpan.getParentId());
	   }
	}

}
